package MyFramePro.Service.Impl;


import MyFramePro.Constants.PageConstants;
import MyFramePro.Constants.SystemConstants;
import MyFramePro.Domain.Dto.AddArticleDto;
import MyFramePro.Domain.Entity.ArticleTag;
import MyFramePro.Domain.Entity.Category;
import MyFramePro.Domain.ResponseResult;
import MyFramePro.Domain.Vo.*;
import MyFramePro.Utils.BeanCopyUtils;
import MyFramePro.Utils.RedisCache;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import MyFramePro.Domain.Entity.Article;
import MyFramePro.Mapper.ArticleMapper;
import MyFramePro.Service.ArticleService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    @Resource
    private ArticleServiceImpl articleService;

    @Resource
    private CategoryServiceImpl categoryService;

    @Resource
    private RedisCache redisCache;

    @Resource
    private ArticleTagServiceImpl articleTagService;


    /**
     * 查询热门文章
     */
    @Override
    public ResponseResult getHotArticleList() {

        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();

        //1.所查询的文章必须是正式的文章，而不是草稿类型的文章
        queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);

        //2.按照文章的浏览量进行排序
        queryWrapper.orderByDesc(Article::getViewCount);

        //3.进行分页查询
        Page<Article> page = new Page<>(PageConstants.PAGE_CURRENT,PageConstants.PAGE_SIZE);  //参数一为当前页数，参数二为每页中显示的记录条数
        page(page,queryWrapper);

        List<Article> articles = page.getRecords(); //这里获取到分页的查询信息


        /**
         * 这里是关于 VO 的优化，即进行 Bean 的拷贝 (因为需求可能只需要其中查询的几个属性传入前端，而不是全部，所以要进行筛选)
         */
        List<HotArticleVo> hotArticleVos
                = BeanCopyUtils.myCopyBeanList(articles, HotArticleVo.class);  //这里使用 Bean 拷贝的工具类

        return ResponseResult.okResult(hotArticleVos);
    }


    /**
     * 这里是进行分页查询文章列表数据
     * @param pageNum    当前页
     * @param pageSize  每页所展示的记录数
     * @param categoryId  文章对应的分类ID
     */
    @Override
    public ResponseResult articleList(Integer pageNum, Integer pageSize, Long categoryId) {

        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();

        if(categoryId!=null&&categoryId>0){
            queryWrapper.eq(Article::getCategoryId,categoryId); //若前端传入的分类ID不为空，则加入 categoryId 字段
        }

        //1.文章的状态需要是正式发布的
        queryWrapper.eq(Article::getStatus,SystemConstants.ARTICLE_STATUS_NORMAL);

        //2.对 isTop 属性进行降序
        queryWrapper.orderByDesc(Article::getIsTop);

        //3.进行分页查询
        Page<Article> articlePage = new Page<>(pageNum,pageSize);
        page(articlePage,queryWrapper);
        List<Article> articleList = articlePage.getRecords();
        //3.1 由于前端需要的数据中包含有 categoryName，所以这里根据 categoryId 查询对应的分类名称
//        for (Article article:articleList){
//            Category category = categoryService.getById(article.getCategoryId());
//            article.setCategoryName(category.getName());    //将分类表中查询到的分类名称，传入文章表中
//        }

        /**
         * 这里将 for 形式转换为 stream 流的形式
         */
        articleList.stream()
                .map((Function<Article, Object>) article -> {
                    Category category = categoryService.getById(article.getCategoryId());
                    article.setCategoryName(category.getName());    //将分类表中查询到的分类名称，传入文章表中
                    return article;
                }).collect(Collectors.toList());

        //4.封装分页查询的结果，进行 Bean 拷贝给 VO 对象
        List<ArticleListVo> articleListVos = BeanCopyUtils.myCopyBeanList(articleList, ArticleListVo.class);

        //5.由于前端需要的数据格式中，rows中包含着分页数据信息，total包含着分页信息的记录总数，所以这里还需要一个 VO 对象来进行封装
        PageVo pageVo = new PageVo(articleListVos,articlePage.getTotal());

        return ResponseResult.okResult(pageVo);
    }


    /**
     * 展示文章的详情信息
     */
    @Override
    public ResponseResult getArticleDetail(Long id) {

        //1.根据传入的 ID 查询对应的文章信息
        Article article = articleService.getById(id);

        //2.使用 VO 对象进行接收
        ArticleDetailVo articleDetailVo = BeanCopyUtils.myCopyBean(article, ArticleDetailVo.class);

        //3.从 redis 中获取当前的浏览量，并传入文章数据中
        Integer viewCount = redisCache.getCacheMapValue("article:viewCount", id.toString()); //接收类型要为 int 类型

        //4.根据分类ID获取分类名称，传入对应的 VO 对象
        Long categoryId = articleDetailVo.getCategoryId();
        Category category = categoryService.getById(categoryId);
        if(category!=null){
            articleDetailVo.setCategoryName(category.getName());
            articleDetailVo.setViewCount(viewCount.longValue());
        }

        return ResponseResult.okResult(articleDetailVo);
    }


    /**
     * 这里是更新对应文章的浏览量的功能
     */
    @Override
    public ResponseResult updateViewCount(Long hKey) {  //这里传入的是 redis 中对应的 hash 键

        //更新浏览量时去更新 redis 中对应文章 ID 的浏览量
        redisCache.incrementCacheMapValue("article:viewCount",hKey.toString(),1);   //value为1表示每次增长1个单位
        return ResponseResult.okResult();
    }


    /**
     * 这里是写博文的功能
     */
    @Override
    public ResponseResult addArticle(AddArticleDto addArticleDto) {
        //1.进行添加博客(将 addArticleDto 中属于 Article 类的字段传入)
        Article article = BeanCopyUtils.myCopyBean(addArticleDto, Article.class);   //由于除了 Dto 中的字段，还需要增改时间，所以进行转换
        articleService.save(article);

        //2.进行获取文章与标签的关联信息(然后将 addArticleDto 中属于 Tag 的字段传入，再封装为 ArticleTag 对象)
        List<ArticleTag> articleTags = addArticleDto.getTags().stream()
                .map(new Function<Long, ArticleTag>() {
                    @Override
                    public ArticleTag apply(Long tagId) {

                        return new ArticleTag(article.getId(), tagId);  //分别添加当前的文章 ID 以及 标签 ID
                    }
                }).collect(Collectors.toList());

        articleTagService.saveBatch(articleTags);

        return ResponseResult.okResult();
    }

    /**
     * 这里是进行文章列表的查询功能
     * @pageNum: 页码
     * @pageSize: 每页条数
     * @title：  文章标题
     * @summary：  文章摘要
     */
    @Override
    public ResponseResult behindArticleList(Integer pageNum, Integer pageSize, String title, String summary) {

        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        //1.进行条件过滤
        queryWrapper.eq(Article::getStatus,SystemConstants.ARTICLE_STATUS_NORMAL);
        queryWrapper.orderByDesc(Article::getIsTop);    //对 isTop 置顶进行降序排列
        queryWrapper.like(StringUtils.hasText(title),Article::getTitle,title);
        queryWrapper.like(StringUtils.hasText(summary),Article::getSummary,summary);

        //2.进行分页查询
        Page<Article> page = new Page<>(pageNum,pageSize);
        articleService.page(page,queryWrapper);

        //3.获取分页查询的数据
        List<Article> articleList = page.getRecords();

        //4.由于前端需求的字段，这里进行 Vo 类型的封装
        List<BehindArticleListVo> behindArticleListVos
                = BeanCopyUtils.myCopyBeanList(articleList, BehindArticleListVo.class); //一转

        PageVo pageVo = new PageVo(behindArticleListVos,page.getTotal());   //二转

        return ResponseResult.okResult(pageVo);
    }


    /**
     * 这里是查询文章详情的功能
     */
    @Override
    public ResponseResult getArticleInfo(Long id) {
        //1.根据 ID 获取当前文章信息
        Article article = articleService.getById(id);

        //2.由于前端需求字段，这里使用 Vo 类型对象进行封装
        ArticleInfoVo articleInfoVo = BeanCopyUtils.myCopyBean(article, ArticleInfoVo.class);

        //3.根据文章 ID 查询对应的标签 ID
        LambdaQueryWrapper<ArticleTag> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ArticleTag::getArticleId,id);

        List<ArticleTag> tagList = articleTagService.list(queryWrapper);

        //4.将查询到的标签 ID 传入 ArticleInfoVo 对象中
        ArrayList<Long> tags = new ArrayList<>();
        tagList.stream()
                .forEach(new Consumer<ArticleTag>() {
                    @Override
                    public void accept(ArticleTag articleTag) {
                        tags.add(articleTag.getTagId());
                    }
                });

        articleInfoVo.setTags(tags);

        return ResponseResult.okResult(articleInfoVo);
    }


    /**
     * 这里是更新文章的功能
     */
    @Override
    public ResponseResult updateArticle(ArticleInfoVo articleInfoVo) {

        //1.分别获取之前未更改的文章信息与标签信息
        Article article1 = articleService.getById(articleInfoVo.getId());   //原来未更改的文章信息

        ArrayList<Long> tags = new ArrayList<>();

        LambdaQueryWrapper<ArticleTag> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ArticleTag::getArticleId,articleInfoVo.getId());
        List<ArticleTag> tagList1 = articleTagService.list(queryWrapper);
        tagList1.stream()
                .forEach(new Consumer<ArticleTag>() {
                    @Override
                    public void accept(ArticleTag articleTag) {
                        tags.add(articleTag.getTagId());
                    }
                });

        //2.获取文章当前的信息
        Article article = BeanCopyUtils.myCopyBean(articleInfoVo, Article.class);   //更改过后的文章信息

        //3.比较之前与现在的文章与标签信息，若有改动则进行更新，反之不进行更新到数据库
        if(!(article.equals(article1)&&articleInfoVo.getTags().equals(tags))) {
            //3.1保存当前文章信息
            articleService.save(article);
            //3.2保存当前标签与文章的 ID 关系
            List<ArticleTag> tagList = articleInfoVo.getTags().stream()
                    .map(new Function<Long, ArticleTag>() {
                        @Override
                        public ArticleTag apply(Long tagId) {

                            return new ArticleTag(article.getId(), tagId);
                        }
                    }).collect(Collectors.toList());

            articleTagService.saveBatch(tagList);

            //4.若当前文章内容进行更新成功，则删除更新之前的文章信息 (逻辑删除)
            articleService.removeById(articleInfoVo.getId().intValue());

        }

        return ResponseResult.okResult();
    }


    /**
     * 这里是删除文章的功能
     */
    @Override
    public ResponseResult deleteArticle(Long id) {

        articleService.removeById(id);

        return ResponseResult.okResult();
    }

}
